home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / share / pyshared / speechd / client.py < prev    next >
Text File  |  2009-09-14  |  34KB  |  877 lines

  1. # Copyright (C) 2003-2008 Brailcom, o.p.s.
  2. #
  3. # This program is free software; you can redistribute it and/or modify
  4. # it under the terms of the GNU General Public License as published by
  5. # the Free Software Foundation; either version 2 of the License, or
  6. # (at your option) any later version.
  7. #
  8. # This program is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11. # GNU General Public License for more details.
  12. #
  13. # You should have received a copy of the GNU General Public License
  14. # along with this program; if not, write to the Free Software
  15. # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  16.  
  17. """Python API to Speech Dispatcher
  18.  
  19. Basic Python client API to Speech Dispatcher is provided by the 'SSIPClient'
  20. class.  This interface maps directly to available SSIP commands and logic.
  21.  
  22. A more convenient interface is provided by the 'Speaker' class.
  23.  
  24. """
  25.  
  26. #TODO: Blocking variants for speak, char, key, sound_icon.
  27.  
  28. import socket, sys, os, subprocess, time
  29.  
  30. # IF session integration has been enabled, the spawn module will be available.
  31. try:
  32.     import spawn
  33. except:
  34.     spawn = None
  35.  
  36. try:
  37.     import threading
  38. except:
  39.     import dummy_threading as threading
  40.  
  41.     
  42. class CallbackType(object):
  43.     """Constants describing the available types of callbacks"""
  44.     INDEX_MARK = 'index_marks'
  45.     """Index mark events are reported when the place they were
  46.     included into the text by the client application is reached
  47.     when speaking them"""
  48.     BEGIN = 'begin'
  49.     """The begin event is reported when Speech Dispatcher starts
  50.     actually speaking the message."""
  51.     END = 'end'
  52.     """The end event is reported after the message has terminated and
  53.     there is no longer any sound from it being produced"""
  54.     CANCEL = 'cancel'
  55.     """The cancel event is reported when a message is canceled either
  56.     on request of the user, because of prioritization of messages or
  57.     due to an error"""
  58.     PAUSE = 'pause'
  59.     """The pause event is reported after speaking of a message
  60.     was paused. It no longer produces any audio."""
  61.     RESUME = 'resume'
  62.     """The resume event is reported right after speaking of a message
  63.     was resumed after previous pause."""
  64.  
  65. class SSIPError(Exception):
  66.     """Common base class for exceptions during SSIP communication."""
  67.     
  68. class SSIPCommunicationError(SSIPError):
  69.     """Exception raised when trying to operate on a closed connection."""
  70.     
  71. class SSIPResponseError(Exception):
  72.     def __init__(self, code, msg, data):
  73.         Exception.__init__(self, "%s: %s" % (code, msg))
  74.         self._code = code
  75.         self._msg = msg
  76.         self._data = data
  77.  
  78.     def code(self):
  79.         """Return the server response error code as integer number."""
  80.         return self._code
  81.         
  82.     def msg(self):
  83.         """Return server response error message as string."""
  84.         return self._msg
  85.  
  86.  
  87. class SSIPCommandError(SSIPResponseError):
  88.     """Exception raised on error response after sending command."""
  89.  
  90.     def command(self):
  91.         """Return the command string which resulted in this error."""
  92.         return self._data
  93.  
  94.     
  95. class SSIPDataError(SSIPResponseError):
  96.     """Exception raised on error response after sending data."""
  97.  
  98.     def data(self):
  99.         """Return the data which resulted in this error."""
  100.         return self._data
  101.  
  102.     
  103. class _SSIP_Connection:
  104.     """Implemantation of low level SSIP communication."""
  105.     
  106.     _NEWLINE = "\r\n"
  107.     _END_OF_DATA_MARKER = '.'
  108.     _END_OF_DATA_MARKER_ESCAPED = '..'
  109.     _END_OF_DATA = _NEWLINE + _END_OF_DATA_MARKER + _NEWLINE
  110.     _END_OF_DATA_ESCAPED = _NEWLINE + _END_OF_DATA_MARKER_ESCAPED + _NEWLINE
  111.  
  112.     _CALLBACK_TYPE_MAP = {700: CallbackType.INDEX_MARK,
  113.                           701: CallbackType.BEGIN,
  114.                           702: CallbackType.END,
  115.                           703: CallbackType.CANCEL,
  116.                           704: CallbackType.PAUSE,
  117.                           705: CallbackType.RESUME,
  118.                           }
  119.     if spawn:
  120.         speechd_server_pid = ''
  121.  
  122.     def __init__(self, host, port):
  123.         """Init connection: open the socket to server,
  124.         initialize buffers, launch a communication handling
  125.         thread."""
  126.         if host == '127.0.0.1' and spawn:
  127.             self.speechd_server_pid = self.speechd_server_spawn()
  128.             time.sleep(0.5)
  129.         self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  130.         self._socket.connect((socket.gethostbyname(host), port))
  131.         self._buffer = ""
  132.         self._com_buffer = []
  133.         self._callback = None
  134.         self._ssip_reply_semaphore = threading.Semaphore(0)
  135.         self._communication_thread = \
  136.                 threading.Thread(target=self._communication, kwargs={},
  137.                                  name="SSIP client communication thread")
  138.         self._communication_thread.start()
  139.     
  140.     def close(self):
  141.         """Close the server connection, destroy the communication thread."""
  142.         # Read-write shutdown here is necessary, otherwise the socket.recv()
  143.         # function in the other thread won't return at last on some platforms.
  144.         try:
  145.             self._socket.shutdown(socket.SHUT_RDWR)
  146.         except socket.error:
  147.             pass
  148.         self._socket.close()
  149.         # Wait for the other thread to terminate
  150.         self._communication_thread.join()
  151.         
  152.     def _communication(self):
  153.         """Handle incomming socket communication.
  154.  
  155.         Listens for all incomming communication on the socket, dispatches
  156.         events and puts all other replies into self._com_buffer list in the
  157.         already parsed form as (code, msg, data).  Each time a new item is
  158.         appended to the _com_buffer list, the corresponding semaphore
  159.         'self._ssip_reply_semaphore' is incremented.
  160.  
  161.         This method is designed to run in a separate thread.  The thread can be
  162.         interrupted by closing the socket on which it is listening for
  163.         reading."""
  164.  
  165.         while True:
  166.             try:
  167.                 code, msg, data = self._recv_message()
  168.             except IOError:
  169.                 # If the socket has been closed, exit the thread
  170.                 sys.exit()
  171.             if code/100 != 7:
  172.                 # This is not an index mark nor an event
  173.                 self._com_buffer.append((code, msg, data))
  174.                 self._ssip_reply_semaphore.release()
  175.                 continue
  176.             # Ignore the event if no callback function has been registered.
  177.             if self._callback is not None:
  178.                 type = self._CALLBACK_TYPE_MAP[code]
  179.                 if type == CallbackType.INDEX_MARK:
  180.                     kwargs = {'index_mark': data[2]}
  181.                 else:
  182.                     kwargs = {}
  183.                 # Get message and client ID of the event
  184.                 msg_id, client_id = map(int, data[:2])
  185.                 self._callback(msg_id, client_id, type, **kwargs)
  186.                 
  187.                 
  188.     def _readline(self):
  189.         """Read one whole line from the socket.
  190.  
  191.         Blocks until the line delimiter ('_NEWLINE') is read.
  192.         
  193.         """
  194.         pointer = self._buffer.find(self._NEWLINE)
  195.         while pointer == -1:
  196.             try:
  197.                 d = self._socket.recv(1024)
  198.             except:
  199.                 raise IOError
  200.             if len(d) == 0:
  201.                 raise IOError
  202.             self._buffer += d
  203.             pointer = self._buffer.find(self._NEWLINE)
  204.         line = self._buffer[:pointer]
  205.         self._buffer = self._buffer[pointer+len(self._NEWLINE):]
  206.         return line
  207.  
  208.     def _recv_message(self):
  209.         """Read server response or a callback
  210.         and return the triplet (code, msg, data)."""
  211.         data = []
  212.         c = None
  213.         while True:
  214.             line = self._readline()
  215.             assert len(line) >= 4, "Malformed data received from server!"
  216.             code, sep, text = line[:3], line[3], line[4:]
  217.             assert code.isalnum() and (c is None or code == c) and \
  218.                    sep in ('-', ' '), "Malformed data received from server!"
  219.             if sep == ' ':
  220.                 msg = text
  221.                 return int(code), msg, tuple(data)
  222.             data.append(text)
  223.  
  224.     def _recv_response(self):
  225.         """Read server response from the communication thread
  226.         and return the triplet (code, msg, data)."""
  227.         # TODO: This check is dumb but seems to work.  The main thread
  228.         # hangs without it, when the Speech Dispatcher connection is lost.
  229.         if not self._communication_thread.isAlive():
  230.             raise SSIPCommunicationError
  231.         self._ssip_reply_semaphore.acquire()
  232.         # The list is sorted, read the first item
  233.         response = self._com_buffer[0]
  234.         del self._com_buffer[0]
  235.         return response
  236.  
  237.     def send_command(self, command, *args):
  238.         """Send SSIP command with given arguments and read server response.
  239.  
  240.         Arguments can be of any data type -- they are all stringified before
  241.         being sent to the server.
  242.  
  243.         Returns a triplet (code, msg, data), where 'code' is a numeric SSIP
  244.         response code as an integer, 'msg' is an SSIP rsponse message as string
  245.         and 'data' is a tuple of strings (all lines of response data) when a
  246.         response contains some data.
  247.         
  248.         'SSIPCommandError' is raised in case of non 2xx return code.  See SSIP
  249.         documentation for more information about server responses and codes.
  250.  
  251.         'IOError' is raised when the socket was closed by the remote side.
  252.         
  253.         """
  254.         if __debug__:
  255.             if command in ('SET', 'CANCEL', 'STOP',):
  256.                 assert args[0] in (Scope.SELF, Scope.ALL) \
  257.                        or isinstance(args[0], int)
  258.         cmd = ' '.join((command,) + tuple(map(str, args)))
  259.         try:
  260.             self._socket.send(cmd + self._NEWLINE)
  261.         except socket.error:
  262.             raise SSIPCommunicationError("Speech Dispatcher connection lost.")
  263.         code, msg, data = self._recv_response()
  264.         if code/100 != 2:
  265.             raise SSIPCommandError(code, msg, cmd)
  266.         return code, msg, data
  267.         
  268.     def send_data(self, data):
  269.         """Send multiline data and read server response.
  270.  
  271.         Returned value is the same as for 'send_command()' method.
  272.  
  273.         'SSIPDataError' is raised in case of non 2xx return code. See SSIP
  274.         documentation for more information about server responses and codes.
  275.         
  276.         'IOError' is raised when the socket was closed by the remote side.
  277.         
  278.         """
  279.         # Escape the end-of-data marker even if present at the beginning
  280.         if data.startswith(self._END_OF_DATA_MARKER + self._NEWLINE):
  281.             l = len(self._END_OF_DATA_MARKER)
  282.             data = self._END_OF_DATA_MARKER_ESCAPED + data[l:]
  283.         elif data == self._END_OF_DATA_MARKER:
  284.             data = self._END_OF_DATA_MARKER_ESCAPED
  285.         data = data.replace(self._END_OF_DATA, self._END_OF_DATA_ESCAPED)
  286.         try:
  287.             self._socket.send(data + self._END_OF_DATA)
  288.         except socket.error:
  289.             raise SSIPCommunicationError("Speech Dispatcher connection lost.")
  290.         code, msg, response_data = self._recv_response()
  291.         if code/100 != 2:
  292.             raise SSIPDataError(code, msg, data)
  293.         return code, msg, response_data
  294.  
  295.     def set_callback(self, callback):
  296.         """Register a callback function for handling asynchronous events.
  297.  
  298.         Arguments:
  299.           callback -- a callable object (function) which will be called to
  300.             handle asynchronous events (arguments described below).  Passing
  301.             `None' results in removing the callback function and ignoring
  302.             events.  Just one callback may be registered.  Attempts to register
  303.             a second callback will result in the former callback being
  304.             replaced.
  305.  
  306.         The callback function must accept three positional arguments
  307.         ('message_id', 'client_id', 'event_type') and an optional keyword
  308.         argument 'index_mark' (when INDEX_MARK events are turned on).
  309.  
  310.         Note, that setting the callback function doesn't turn the events on.
  311.         The user is responsible to turn them on by sending the appropriate `SET
  312.         NOTIFICATION' command.
  313.  
  314.         """
  315.         self._callback = callback
  316.  
  317.     def speechd_server_spawn(self):
  318.         """Attempts to spawn the speech-dispatcher server."""
  319.         if os.path.exists(spawn.SPD_SPAWN_CMD):
  320.             speechd_server = subprocess.Popen([spawn.SPD_SPAWN_CMD],
  321.                         stdin=None, stdout=subprocess.PIPE, stderr=None)
  322.             return speechd_server.communicate()[0].rstrip('\n')
  323.  
  324. class Scope(object):
  325.     """An enumeration of valid SSIP command scopes.
  326.  
  327.     The constants of this class should be used to specify the 'scope' argument
  328.     for the 'Client' methods.
  329.  
  330.     """    
  331.     SELF = 'self'
  332.     """The command (mostly a setting) applies to current connection only."""
  333.     ALL = 'all'
  334.     """The command applies to all current Speech Dispatcher connections."""
  335.  
  336.     
  337. class Priority(object):
  338.     """An enumeration of valid SSIP message priorities.
  339.  
  340.     The constants of this class should be used to specify the 'priority'
  341.     argument for the 'Client' methods.  For more information about message
  342.     priorities and their interaction, see the SSIP documentation.
  343.     
  344.     """
  345.     IMPORTANT = 'important'
  346.     TEXT = 'text'
  347.     MESSAGE = 'message'
  348.     NOTIFICATION = 'notification'
  349.     PROGRESS = 'progress'
  350.  
  351.     
  352. class PunctuationMode(object):
  353.     """Constants for selecting a punctuation mode.
  354.  
  355.     The mode determines which characters should be read.
  356.  
  357.     """
  358.     ALL = 'all'
  359.     """Read all punctuation characters."""
  360.     NONE = 'none'
  361.     """Don't read any punctuation character at all."""
  362.     SOME = 'some'
  363.     """Only the user-defined punctuation characters are read.
  364.  
  365.     The set of characters is specified in Speech Dispatcher configuration.
  366.  
  367.     """
  368.  
  369. class SSIPClient(object):
  370.     """Basic Speech Dispatcher client interface.
  371.  
  372.     This class provides a Python interface to Speech Dispatcher functionality
  373.     over an SSIP connection.  The API maps directly to available SSIP commands.
  374.     Each connection to Speech Dispatcher is represented by one instance of this
  375.     class.
  376.     
  377.     Many commands take the 'scope' argument, thus it is shortly documented
  378.     here.  It is either one of 'Scope' constants or a number of connection.  By
  379.     specifying the connection number, you are applying the command to a
  380.     particular connection.  This feature is only meant to be used by Speech
  381.     Dispatcher control application, however.  More datails can be found in
  382.     Speech Dispatcher documentation.
  383.  
  384.     """
  385.     
  386.     DEFAULT_SPEECHD_HOST = '127.0.0.1'
  387.     """Default host for server connections."""
  388.     DEFAULT_SPEECHD_PORT = 6560
  389.     """Default port number for server connections."""
  390.     
  391.     def __init__(self, name, component='default', user='unknown', host=None,
  392.                  port=None):
  393.         """Initialize the instance and connect to the server.
  394.  
  395.         Arguments:
  396.           name -- client identification string
  397.           component -- connection identification string.  When one client opens
  398.             multiple connections, this can be used to identify each of them.
  399.           user -- user identification string (user name).  When multi-user
  400.             acces is expected, this can be used to identify their connections.
  401.           host -- server hostname or IP address as a string.  If None, the
  402.             default value is taken from SPEECHD_HOST environment variable (if it
  403.             exists) or from the DEFAULT_SPEECHD_HOST attribute of this class.
  404.           port -- server port as number or None.  If None, the default value is
  405.             taken from SPEECHD_PORT environment variable (if it exists) or from the
  406.             DEFAULT_SPEECHD_PORT attribute of this class.
  407.         
  408.         For more information on client identification strings see Speech
  409.         Dispatcher documentation.
  410.           
  411.         """
  412.         if host is None:
  413.             host = os.environ.get('SPEECHD_HOST', self.DEFAULT_SPEECHD_HOST)
  414.         if port is None:
  415.             try:
  416.                 port = int(os.environ.get('SPEECHD_PORT'))
  417.             except (ValueError, TypeError):
  418.                 port = self.DEFAULT_SPEECHD_PORT
  419.         self._conn = conn = _SSIP_Connection(host, port)
  420.         full_name = '%s:%s:%s' % (user, name, component)
  421.         conn.send_command('SET', Scope.SELF, 'CLIENT_NAME', full_name)
  422.         code, msg, data = conn.send_command('HISTORY', 'GET', 'CLIENT_ID')
  423.         self._client_id = int(data[0])
  424.         self._lock = threading.Lock()
  425.         self._callbacks = {}
  426.         conn.set_callback(self._callback_handler)
  427.         for event in (CallbackType.INDEX_MARK,
  428.                       CallbackType.BEGIN,
  429.                       CallbackType.END,
  430.                       CallbackType.CANCEL,
  431.                       CallbackType.PAUSE,
  432.                       CallbackType.RESUME):
  433.             conn.send_command('SET', 'self', 'NOTIFICATION', event, 'on')
  434.  
  435.     
  436.     def __del__(self):
  437.         """Close the connection"""
  438.         self.close()
  439.  
  440.     def _callback_handler(self, msg_id, client_id, type, **kwargs):
  441.         if client_id != self._client_id:
  442.             # TODO: does that ever happen?
  443.             return
  444.         self._lock.acquire()
  445.         try:
  446.             try:
  447.                 callback, event_types = self._callbacks[msg_id]
  448.             except KeyError:
  449.                 pass
  450.             else:
  451.                 if event_types is None or type in event_types:
  452.                     callback(type, **kwargs)
  453.                 if type in (CallbackType.END, CallbackType.CANCEL):
  454.                     del self._callbacks[msg_id]
  455.         finally:
  456.             self._lock.release()
  457.                 
  458.         
  459.     def set_priority(self, priority):
  460.         """Set the priority category for the following messages.
  461.  
  462.         Arguments:
  463.           priority -- one of the 'Priority' constants.
  464.  
  465.         """
  466.         assert priority in (Priority.IMPORTANT, Priority.MESSAGE,
  467.                             Priority.TEXT, Priority.NOTIFICATION,
  468.                             Priority.PROGRESS), priority
  469.         self._conn.send_command('SET', Scope.SELF, 'PRIORITY', priority)
  470.  
  471.     def speak(self, text, callback=None, event_types=None):
  472.         """Say given message.
  473.  
  474.         Arguments:
  475.           text -- message text to be spoken.  This may be either a UTF-8
  476.             encoded byte string or a Python unicode string.
  477.           callback -- a callback handler for asynchronous event notifications.
  478.             A callable object (function) which accepts one positional argument
  479.             `type' and one keyword argument `index_mark'.  See below for more
  480.             details.
  481.           event_types -- a tuple of event types for which the callback should
  482.             be called.  Each item must be one of `CallbackType' constants.
  483.             None (the default value) means to handle all event types.  This
  484.             argument is irrelevant when `callback' is not used.
  485.  
  486.         The callback function will be called whenever one of the events occurs.
  487.         The event type will be passed as argument.  Its value is one of the
  488.         `CallbackType' constants.  In case of an index mark event, additional
  489.         keyword argument `index_mark' will be passed and will contain the index
  490.         mark identifier as specified within the text.
  491.  
  492.         The callback function should not perform anything complicated and is
  493.         not allowed to issue any further SSIP client commands.  An attempt to
  494.         do so would lead to a deadlock in SSIP communication.
  495.  
  496.         This method is non-blocking;  it just sends the command, given
  497.         message is queued on the server and the method returns immediately.
  498.  
  499.         """
  500.         self._conn.send_command('SPEAK')
  501.         if isinstance(text, unicode):
  502.             text = text.encode('utf-8')
  503.         result = self._conn.send_data(text)
  504.         if callback:
  505.             msg_id = int(result[2][0])
  506.             # TODO: Here we risk, that the callback arrives earlier, than we
  507.             # add the item to `self._callbacks'.  Such a situation will lead to
  508.             # the callback being ignored.
  509.             self._lock.acquire()
  510.             try:
  511.                 self._callbacks[msg_id] = (callback, event_types)
  512.             finally:
  513.                 self._lock.release()
  514.         return result
  515.  
  516.     def char(self, char):
  517.         """Say given character.
  518.  
  519.         Arguments:
  520.           char -- a character to be spoken.  Either a Python unicode string or
  521.             a UTF-8 encoded byte string.
  522.  
  523.         This method is non-blocking;  it just sends the command, given
  524.         message is queued on the server and the method returns immediately.
  525.  
  526.         """
  527.         if isinstance(char, unicode):
  528.             char = char.encode('utf-8')
  529.         self._conn.send_command('CHAR', char.replace(' ', 'space'))
  530.         
  531.     def key(self, key):
  532.         """Say given key name.
  533.  
  534.         Arguments:
  535.           key -- the key name (as defined in SSIP); string.
  536.  
  537.         This method is non-blocking;  it just sends the command, given
  538.         message is queued on the server and the method returns immediately.
  539.  
  540.         """
  541.         self._conn.send_command('KEY', key)
  542.  
  543.     def sound_icon(self, sound_icon):
  544.         """Output given sound_icon.
  545.  
  546.         Arguments:
  547.           sound_icon -- the name of the sound icon as defined by SSIP; string.
  548.  
  549.         This method is non-blocking; it just sends the command, given message
  550.         is queued on the server and the method returns immediately.
  551.  
  552.         """        
  553.         self._conn.send_command('SOUND_ICON', sound_icon)
  554.                     
  555.     def cancel(self, scope=Scope.SELF):
  556.         """Immediately stop speaking and discard messages in queues.
  557.  
  558.         Arguments:
  559.           scope -- see the documentaion of this class.
  560.             
  561.         """
  562.         self._conn.send_command('CANCEL', scope)
  563.  
  564.  
  565.     def stop(self, scope=Scope.SELF):
  566.         """Immediately stop speaking the currently spoken message.
  567.  
  568.         Arguments:
  569.           scope -- see the documentaion of this class.
  570.         
  571.         """
  572.         self._conn.send_command('STOP', scope)
  573.  
  574.     def pause(self, scope=Scope.SELF):
  575.         """Pause speaking and postpone other messages until resume.
  576.  
  577.         This method is non-blocking.  However, speaking can continue for a
  578.         short while even after it's called (typically to the end of the
  579.         sentence).
  580.  
  581.         Arguments:
  582.           scope -- see the documentaion of this class.
  583.         
  584.         """
  585.         self._conn.send_command('PAUSE', scope)
  586.  
  587.     def resume(self, scope=Scope.SELF):
  588.         """Resume speaking of the currently paused messages.
  589.  
  590.         This method is non-blocking.  However, speaking can continue for a
  591.         short while even after it's called (typically to the end of the
  592.         sentence).
  593.  
  594.         Arguments:
  595.           scope -- see the documentaion of this class.
  596.         
  597.         """
  598.         self._conn.send_command('RESUME', scope)
  599.  
  600.     def list_output_modules(self):
  601.         """Return names of all active output modules as a tuple of strings."""
  602.         code, msg, data = self._conn.send_command('LIST', 'OUTPUT_MODULES')
  603.         return data
  604.  
  605.     def list_synthesis_voices(self):
  606.         """Return names of all available voices for the current output module.
  607.  
  608.         Returns a tuple of tripplets (name, language, dialect).
  609.  
  610.         'name' is a string, 'language' is an ISO 639-1 Alpha-2 language code
  611.         and 'dialect' is a string.  Language and dialect may be None.
  612.  
  613.         """
  614.         try:
  615.             code, msg, data = self._conn.send_command('LIST', 'SYNTHESIS_VOICES')
  616.         except SSIPCommandError:
  617.             return ()
  618.         def split(item):
  619.             name, lang, dialect = tuple(item.rsplit(' ', 3))
  620.             return (name, lang or None, dialect or None)
  621.         return tuple([split(item) for item in data])
  622.  
  623.     def set_language(self, language, scope=Scope.SELF):
  624.         """Switch to a particular language for further speech commands.
  625.  
  626.         Arguments:
  627.           language -- two letter language code according to RFC 1776 as string.
  628.           scope -- see the documentaion of this class.
  629.             
  630.         """
  631.         assert isinstance(language, str) and len(language) == 2
  632.         self._conn.send_command('SET', scope, 'LANGUAGE', language)
  633.  
  634.     def set_output_module(self, name, scope=Scope.SELF):
  635.         """Switch to a particular output module.
  636.  
  637.         Arguments:
  638.           name -- module (string) as returned by 'list_output_modules()'.
  639.           scope -- see the documentaion of this class.
  640.         
  641.         """
  642.         self._conn.send_command('SET', scope, 'OUTPUT_MODULE', name)
  643.  
  644.     def set_pitch(self, value, scope=Scope.SELF):
  645.         """Set the pitch for further speech commands.
  646.  
  647.         Arguments:
  648.           value -- integer value within the range from -100 to 100, with 0
  649.             corresponding to the default pitch of the current speech synthesis
  650.             output module, lower values meaning lower pitch and higher values
  651.             meaning higher pitch.
  652.           scope -- see the documentaion of this class.
  653.           
  654.         """
  655.         assert isinstance(value, int) and -100 <= value <= 100, value
  656.         self._conn.send_command('SET', scope, 'PITCH', value)
  657.  
  658.     def set_rate(self, value, scope=Scope.SELF):
  659.         """Set the speech rate (speed) for further speech commands.
  660.  
  661.         Arguments:
  662.           value -- integer value within the range from -100 to 100, with 0
  663.             corresponding to the default speech rate of the current speech
  664.             synthesis output module, lower values meaning slower speech and
  665.             higher values meaning faster speech.
  666.           scope -- see the documentaion of this class.
  667.             
  668.         """
  669.         assert isinstance(value, int) and -100 <= value <= 100
  670.         self._conn.send_command('SET', scope, 'RATE', value)
  671.  
  672.     def set_volume(self, value, scope=Scope.SELF):
  673.         """Set the speech volume for further speech commands.
  674.  
  675.         Arguments:
  676.           value -- integer value within the range from -100 to 100, with 100
  677.             corresponding to the default speech volume of the current speech
  678.             synthesis output module, lower values meaning softer speech.
  679.           scope -- see the documentaion of this class.
  680.             
  681.         """
  682.         assert isinstance(value, int) and -100 <= value <= 100
  683.         self._conn.send_command('SET', scope, 'VOLUME', value)
  684.  
  685.     def set_punctuation(self, value, scope=Scope.SELF):
  686.         """Set the punctuation pronounciation level.
  687.  
  688.         Arguments:
  689.           value -- one of the 'PunctuationMode' constants.
  690.           scope -- see the documentaion of this class.
  691.             
  692.         """
  693.         assert value in (PunctuationMode.ALL, PunctuationMode.SOME,
  694.                          PunctuationMode.NONE), value
  695.         self._conn.send_command('SET', scope, 'PUNCTUATION', value)
  696.  
  697.     def set_spelling(self, value, scope=Scope.SELF):
  698.         """Toogle the spelling mode or on off.
  699.  
  700.         Arguments:
  701.           value -- if 'True', all incomming messages will be spelled
  702.             instead of being read as normal words. 'False' switches
  703.             this behavior off.
  704.           scope -- see the documentaion of this class.
  705.             
  706.         """
  707.         assert value in [True, False]
  708.         if value == True:
  709.             self._conn.send_command('SET', scope, 'SPELLING', "on")
  710.         else:
  711.             self._conn.send_command('SET', scope, 'SPELLING', "off")
  712.  
  713.     def set_cap_let_recogn(self, value, scope=Scope.SELF):
  714.         """Set capital letter recognition mode.
  715.  
  716.         Arguments:
  717.           value -- one of 'none', 'spell', 'icon'. None means no signalization
  718.             of capital letters, 'spell' means capital letters will be spelled
  719.             with a syntetic voice and 'icon' means that the capital-letter icon
  720.             will be prepended before each capital letter.
  721.           scope -- see the documentaion of this class.
  722.             
  723.         """
  724.         assert value in ("none", "spell", "icon")
  725.         self._conn.send_command('SET', scope, 'CAP_LET_RECOGN', value)
  726.  
  727.     def set_voice(self, value, scope=Scope.SELF):
  728.         """Set voice by a symbolic name.
  729.  
  730.         Arguments:
  731.           value -- one of the SSIP symbolic voice names: 'MALE1' .. 'MALE3',
  732.             'FEMALE1' ... 'FEMALE3', 'CHILD_MALE', 'CHILD_FEMALE'
  733.           scope -- see the documentaion of this class.
  734.  
  735.         Symbolic voice names are mapped to real synthesizer voices in the
  736.         configuration of the output module.  Use the method
  737.         'set_synthesis_voice()' if you want to work with real voices.
  738.             
  739.         """
  740.         assert isinstance(value, str) and \
  741.                value.lower() in ("male1", "male2", "male3", "female1",
  742.                                  "female2", "female3", "child_male",
  743.                                  "child_female")
  744.         self._conn.send_command('SET', scope, 'VOICE', value)
  745.  
  746.     def set_synthesis_voice(self, value, scope=Scope.SELF):
  747.         """Set voice by its real name.
  748.  
  749.         Arguments:
  750.           value -- voice name as returned by 'list_synthesis_voices()'
  751.           scope -- see the documentaion of this class.
  752.             
  753.         """
  754.         self._conn.send_command('SET', scope, 'SYNTHESIS_VOICE', value)
  755.         
  756.     def set_pause_context(self, value, scope=Scope.SELF):
  757.         """Set the amount of context when resuming a paused message.
  758.  
  759.         Arguments:
  760.           value -- a positive or negative value meaning how many chunks of data
  761.             after or before the pause should be read when resume() is executed.
  762.           scope -- see the documentaion of this class.
  763.             
  764.         """
  765.         assert isinstance(value, int)
  766.         self._conn.send_command('SET', scope, 'PAUSE_CONTEXT', value)
  767.  
  768.     def set_debug(self, val):
  769.         """Switch debugging on and off. When switched on,
  770.         debugging files will be created in the chosen destination
  771.         (see set_debug_destination()) for Speech Dispatcher and all
  772.         its running modules. All logging information will then be
  773.         written into these files with maximal verbosity until switched
  774.         off. You should always first call set_debug_destination.
  775.  
  776.         The intended use of this functionality is to switch debuging
  777.         on for a period of time while the user will repeat the behavior
  778.         and then send the logs to the appropriate bug-reporting place.
  779.  
  780.         Arguments:
  781.           val -- a boolean value determining whether debugging
  782.                  is switched on or off
  783.           scope -- see the documentaion of this class.
  784.         
  785.         """
  786.         assert isinstance(val, bool)
  787.         if val == True:
  788.             ssip_val = "ON"
  789.         else:
  790.             ssip_val = "OFF"
  791.  
  792.         self._conn.send_command('SET', scope.ALL, 'DEBUG', ssip_val)
  793.  
  794.  
  795.     def set_debug_destination(self, path):
  796.         """Set debug destination.
  797.  
  798.         Arguments:
  799.           path -- path (string) to the directory where debuging
  800.                   files will be created
  801.           scope -- see the documentaion of this class.
  802.         
  803.         """
  804.         assert isinstance(val, string)
  805.  
  806.         self._conn.send_command('SET', scope.ALL, 'DEBUG_DESTINATION', val)
  807.  
  808.     def block_begin(self):
  809.         """Begin an SSIP block.
  810.  
  811.         See SSIP documentation for more details about blocks.
  812.  
  813.         """
  814.         self._conn.send_command('BLOCK', 'BEGIN')
  815.  
  816.     def block_end(self):
  817.         """Close an SSIP block.
  818.  
  819.         See SSIP documentation for more details about blocks.
  820.  
  821.         """
  822.         self._conn.send_command('BLOCK', 'END')
  823.  
  824.     def close(self):
  825.         """Close the connection to Speech Dispatcher."""
  826.         if hasattr(self, '_conn'):
  827.             self._conn.close()
  828.  
  829.  
  830. class Client(SSIPClient):
  831.     """A DEPRECATED backwards-compatible API.
  832.  
  833.     This Class is provided only for backwards compatibility with the prevoius
  834.     unofficial API.  It will be removed in future versions.  Please use either
  835.     'SSIPClient' or 'Speaker' interface instead.  As deprecated, the API is no
  836.     longer documented.
  837.  
  838.     """
  839.     def __init__(self, name=None, client=None, **kwargs):
  840.         name = name or client or 'python'
  841.         super(Client, self).__init__(name, **kwargs)
  842.         
  843.     def say(self, text, priority=Priority.MESSAGE):
  844.         self.set_priority(priority)
  845.         self.speak(text)
  846.  
  847.     def char(self, char, priority=Priority.TEXT):
  848.         self.set_priority(priority)
  849.         super(Client, self).char(char)
  850.  
  851.     def key(self, key, priority=Priority.TEXT):
  852.         self.set_priority(priority)
  853.         super(Client, self).key(key)
  854.  
  855.     def sound_icon(self, sound_icon, priority=Priority.TEXT):
  856.         self.set_priority(priority)
  857.         super(Client, self).sound_icon(sound_icon)
  858.         
  859.  
  860. class Speaker(SSIPClient):
  861.     """Extended Speech Dispatcher Interface.
  862.  
  863.     This class provides an extended intercace to Speech Dispatcher
  864.     functionality and tries to hide most of the lower level details of SSIP
  865.     (such as a more sophisticated handling of blocks and priorities and
  866.     advanced event notifications) under a more convenient API.
  867.     
  868.     Please note that the API is not yet stabilized and thus is subject to
  869.     change!  Please contact the authors if you plan using it and/or if you have
  870.     any suggestions.
  871.  
  872.     Well, in fact this class is currently not implemented at all.  It is just a
  873.     draft.  The intention is to hide the SSIP details and provide a generic
  874.     interface practical for screen readers.
  875.     
  876.     """
  877.